package com.audaque.springboot.foshanupload.jwtvalid.component;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.json.JSONUtil;
import com.audaque.springboot.foshanupload.authcore.model.currentUser.TransmittableThreadLocalCurrentUser;
import com.audaque.springboot.foshanupload.jwtcore.component.iface.JwtProviderIface;
import com.audaque.springboot.foshanupload.jwtcore.constants.JwtConst;
import com.audaque.springboot.foshanupload.jwtcore.properties.JwtProperties;
import com.audaque.springboot.foshanupload.jwtcore.util.encode.rsaPem.RsaPem;
import com.audaque.springboot.foshanupload.jwtcore.util.encode.rsaPem.RsaUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;

/**
 * @author zgb
 * @desc ...
 * @date 2023-07-14 15:20:42
 */
@Component
@Slf4j
public class JwtLoginProvider {
    @Autowired
    private JwtProviderIface jwtProviderIface;

    @Autowired
    private JwtProperties jwtProperties;


    @Value("${db.tenantId}")
    private String tenantId;


    public void login(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) {

        String accessTokenKey = jwtProperties.getAccessTokenKey();
        String accessToken = httpServletRequest.getHeader(accessTokenKey);
        //没有则放行
        if (StringUtils.isBlank(accessToken)) {
            return;
        }

        //每当用户想要访问受保护的路由或资源时，用户将使用承载【bearer】模式发送JWT，通常在Authorization标头中。标题的内容应如下所示： Authorization: Bearer <token>
        //使用token时手动添加前缀
        if (!accessToken.startsWith(JwtConst.ACCESSTOKEN_STARTS_WITH)) {
            return;
        }

        //去除Bearer 后部分
        accessToken = accessToken.substring(7);

        if (StringUtils.isBlank(accessToken)) {
            return;
        }


        //解密token，拿到里面的对象claims
        String body = null;
        try {
            body = jwtProviderIface.getBody(accessToken);
        } catch (Exception e) {
            return;
        }
        if (StringUtils.isBlank(body)) {
            return;
        }
        //无状态
        TransmittableThreadLocalCurrentUser transmittableThreadLocalCurrentUser = JSONUtil.toBean(body, TransmittableThreadLocalCurrentUser.class);
        transmittableThreadLocalCurrentUser.setAccessToken(accessToken);

        String enId = transmittableThreadLocalCurrentUser.getId();
        String enUsername = transmittableThreadLocalCurrentUser.getUsername();
        String privateKey = jwtProperties.getPrivateKey();
        String deId = null;
        String deUsername = null;
        try {
            RsaUtil rsa = new RsaUtil(RsaPem.FromPEM(privateKey));
            //解密userId
            deId = rsa.Decode(enId);
            deUsername = rsa.Decode(enUsername);
        } catch (Exception e) {
            e.printStackTrace();
            log.error(String.format("jwt:claims:id:解密失败：%s", e.getMessage()));
            return;
        }
        transmittableThreadLocalCurrentUser.setId(deId);
        transmittableThreadLocalCurrentUser.setTenantId(tenantId);
        transmittableThreadLocalCurrentUser.setUsername(deUsername);

        //刷新token
        //jwt的负载要加密
        Map<String, Object> map = BeanUtil.beanToMap(transmittableThreadLocalCurrentUser);
        map.put("verify", "verify");
        String accessToken1 = null;
        try {
            accessToken1 = jwtProviderIface.getAccessToken(map);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        String refreshTokenKey = jwtProperties.getRefreshTokenKey();
        httpServletResponse.setHeader(refreshTokenKey, accessToken1);
        //添加当前用户到上下文
        TransmittableThreadLocalCurrentUser.setCurrentUser(transmittableThreadLocalCurrentUser);

        Thread thread = Thread.currentThread();
        long threadId = thread.getId();
        log.debug(String.format("线程id:%s", threadId));
        return;
    }


}
